/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package net.worlds.input;

import java.awt.Color;
import java.awt.event.KeyEvent;
import java.util.List;
import net.worlds.WorldsException;
import net.worlds.WorldsSettings;
import net.worlds.Main;
import net.worlds.RedoRunnable;
import net.worlds.World;
import net.worlds.Stats;
import net.worlds.UndoRunnable;
import net.worlds.Util;
import net.worlds.object.AssetService;
import net.worlds.ui.UI;
import net.worlds.object.Box;
import net.worlds.object.Circle;
import net.worlds.object.GameObject;
import net.worlds.object.Ladder;
import net.worlds.object.Part;
import net.worlds.object.Rope;
import net.worlds.object.Seat;
import net.worlds.object.Terrain;
import net.worlds.object.Triangle;
import net.worlds.object.Water;
import org.jbox2d.collision.AABB;
import org.jbox2d.collision.shapes.PolygonDef;
import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.common.Vec2D;
import org.jbox2d.dynamics.joints.MouseJoint;
import org.jbox2d.dynamics.joints.MouseJointDef;


public class DefaultInputProcessor extends InputProcessor{
    private MouseJointDef md = null;
    private MouseJoint mj = null;
    @Override
    public void keyDown(int code) throws WorldsException {
        if(code == KeyEvent.VK_F7)
        {
            Stats.showStats = true;
        }
    }

    @Override
    public void keyUp(int code) throws WorldsException {
        if(code == KeyEvent.VK_SHIFT)
        {
            if(Main.canUseTools)
                UI.shiftPressed = false;
        }
        if(Main.getLocalPlayer() != null)
            Main.getLocalPlayer().processKeyUp(code);
        if(code > 31 && Main.getLocalPlayer() != null && Main.getLocalPlayer().getSelectedTool() != null)
        {
            if(XKeyListener.getKeyChar(code) != null)
                Main.getLocalPlayer().getSelectedTool().keyUp(XKeyListener.getKeyChar(code));
        }
        else if(code == KeyEvent.VK_F7)
        {
            Stats.showStats = false;
        }
    }

    @Override
    public void keyTyped(int code) throws WorldsException {
        if(code > 31 && Main.getLocalPlayer() != null && Main.getLocalPlayer().getSelectedTool() != null && !Main.chatBarActive)
        {
            Main.getLocalPlayer().getSelectedTool().keyDown(XKeyListener.getKeyChar(code));
        }
        if(InputState.pressedKeyCodes.contains(KeyEvent.VK_CONTROL))
        {
            Runnable r = UI.getKeyboardShortcut(code);
            if(r != null)
                r.run();
        }
        if(code == KeyEvent.VK_DELETE && !Main.chatBarActive)
        {
            if(Main.canUseTools)
                UI.deleteSelection();
        }
        else if(code == KeyEvent.VK_PLUS || code == KeyEvent.VK_EQUALS || code == KeyEvent.VK_ADD)
        {
            if(Main.getLocalPlayer() == null)
            {
                ((AssetService)Main.getGame().getMainWorld().getService("AssetService")).playSound("Click2");
                Main.getView().setScaleFactor(Main.getView().scaleFactor + Main.getView().scaleFactor / 10);
            }
        }
        else if(code == KeyEvent.VK_MINUS || code == KeyEvent.VK_SUBTRACT)
        {
            if(Main.getLocalPlayer() == null)
            {
                ((AssetService)Main.getGame().getMainWorld().getService("AssetService")).playSound("Click2");
                Main.getView().setScaleFactor(Main.getView().scaleFactor - Main.getView().scaleFactor / 10);
            }
        }
        else if(code == KeyEvent.VK_SHIFT)
        {
            if(Main.canUseTools)
                UI.shiftPressed = true;
        }
        else if(Util.isInteger(KeyEvent.getKeyText(code)) && Main.getLocalPlayer() != null && !Main.chatBarActive)
        {
            Main.getLocalPlayer().setSelectedTool(Integer.parseInt(KeyEvent.getKeyText(code)) - 1);
        }
        if(Main.getLocalPlayer() != null && !Main.chatBarActive)
            Main.getLocalPlayer().processKeyDown(code);
    }

    @Override
    public void mouseDown(int b, int x, int y) throws WorldsException {
        World world = Main.getGame().getCurrentWorld();
        if(Main.getView().eMouseDown(b, x, y + 55))
        {
            UI.mouseClickUI = true;
            return;
        }
        else
            UI.mouseClickUI = false;
        Part clickedPart = null;
        Vec2D clickVec = new Vec2D(x, y).screenToWorld();
        if(Main.getLocalPlayer() != null && Main.getLocalPlayer().getSelectedTool() != null)
        {
            Main.getLocalPlayer().getSelectedTool().mouseDown(b, clickVec);
        }
        synchronized(world.getObjectsByType(Box.class))
        {
            for(GameObject obj : world.getParts())
            {
                Part p = (Part) obj;
                if(p != null && p.getBody() != null && !p.disabled && !(UI.drawOver && (UI.mouseMode == UI.MODE_CIRCLE || UI.mouseMode == UI.MODE_BOX)) && p.getShape().testPoint(((Part)obj).getBody().getXForm(), clickVec))
                {
                    if(Main.isClient())
                        Main.getClient().sendClick(p, b);
                    else
                        p.getEvent("Clicked").fire(new Object[]{Main.getPlayer(), b});
                }
            }
        }
        if(b == 1 && UI.mouseMode != UI.MODE_NONE)
        {
            boolean clickedJoint = false;
            for(GameObject j : world.getJoints())
            {
                Vec2D pos = ((Vec2D) j.getProp("Anchor")).worldToScreen(Main.getView());
                Vec2D pos2 = new Vec2D(-1000, -1000);
                if(j instanceof Rope)
                    pos2 = j.getVec2DProp("Anchor2").worldToScreen(Main.getView());
                if(!j.disabled && ((x > pos.x - 5 && y > pos.y - 5 && x < pos.x+ 5 && y < pos.y + 5)||(x > pos2.x - 5 && y > pos2.y - 5 && x < pos2.x+ 5 && y < pos2.y + 5)))
                {
                    if(UI.mouseMode == UI.MODE_DELETE)
                    {
                        j.remove();
                        clickedJoint = true;
                        break;
                    }
                    if(!UI.shiftPressed)
                    {
                        UI.clearSelection(false);
                        UI.select(j);
                    }
                    else
                    {
                        if(!j.selected)
                            UI.select(j);
                        else
                            UI.deselect(j);
                    }
                    clickedJoint = true;
                    break;
                }
            }
            if(clickedJoint && Main.getLocalPlayer() == null)
            {
                UI.updateSelection();
                return;
            }

            Vec2D worldVec = new Vec2D(x, y).screenToWorld();
            int found = 0;
            boolean done = false;
            if(UI.mouseMode != UI.MODE_ROPE)
            {
                for(int i = 2; i >= 0; i--)
                {
                    List<GameObject> layer = world.getLayer(i);
                    synchronized(layer)
                    {
                        for(int j = layer.size() - 1; j >= 0; j--)
                        {
                            GameObject obj = layer.get(j);
                            if(!(obj instanceof Part))
                                continue;
                            Part p = (Part) obj;
                            if(!p.disabled && p.getBody() != null && !(UI.drawOver && (UI.mouseMode == UI.MODE_CIRCLE || UI.mouseMode == UI.MODE_BOX)) && !obj.getBooleanProp("Locked") && p.getShape().testPoint(((Part)obj).getBody().getXForm(), worldVec))
                            {
                                if(UI.mouseMode != UI.MODE_DELETE)
                                {
                                    p.setOldRotation();
                                    clickedPart = p;
                                    if(!p.selected && InputState.pmouseButton == 0)
                                    {
                                        if(!UI.shiftPressed)
                                            UI.clearSelection(false);
                                        UI.select(obj);
                                        obj.newlySelected = true;
                                        found++;
                                        done = true;
                                        break;
                                    }
                                    else
                                    {
                                        found++;
                                        done = true;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    if(done)
                        break;
                }
            }
            boolean foundHandle = false;
            if(UI.mouseMode == UI.MODE_SCALE)
            {
                List<GameObject> selected = UI.getSelected();
                synchronized(selected)
                {
                    for(GameObject obj : selected)
                    {
                        if(!obj.disabled && obj instanceof Box && !obj.getBooleanProp("Locked") && !(obj instanceof Ladder))
                        {
                            Box box = (Box) obj;
                            Vec2D[] rv = PolygonDef.rotate(((PolygonShape)box.getShape()).m_vertices, box.rotationDiff);
                            for(Vec2D v : rv)
                            {
                                Vec2D screenVert = new Vec2D(box.getBody().getWorldPoint(v)).worldToScreen(Main.getView());
                                if(x > screenVert.x - 4 && x < screenVert.x + 4 && y > screenVert.y - 4 && y < screenVert.y + 4)
                                {
                                    foundHandle = true;
                                    UI.scaleBox = box;
                                    box.setInUse(true);
                                    box.getBody().resetFiltering();
                                    UI.scaleVert = v;
                                    for(Vec2D v2 : rv)
                                    {
                                        if(Math.abs(v.x - v2.x)> 0.01 && Math.abs(v.y - v2.y) > 0.01)
                                            UI.scaleOppositeVert = v2;
                                    }
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            if(found == 0 && !UI.shiftPressed && !foundHandle && UI.mouseMode != UI.MODE_ROPE)
            {
                UI.clearSelection(false);
            }
            UI.updateSelection();

            if(found == 0)
            {
                if(UI.mouseMode == UI.MODE_BOX || UI.mouseMode == UI.MODE_WATER || UI.mouseMode == UI.MODE_LADDER)
                    UI.drawingBox = true;
                else if(UI.mouseMode == UI.MODE_CIRCLE)
                    UI.drawingCircle = true;
                else if(UI.mouseMode == UI.MODE_TRIANGLE)
                    UI.drawingTriangle = true;
                if(UI.mouseMode == UI.MODE_WATER)
                    UI.setNextObjectColor(UI.waterColor);
                else if(UI.mouseMode == UI.MODE_LADDER)
                    UI.setNextObjectColor(UI.ladderColor);
                else
                    UI.setNextObjectColor(Util.randomColor());
            }

            if((UI.mouseMode == UI.MODE_HINGE  || UI.mouseMode == UI.MODE_WELD) && !UI.isDraggingPart())
            {
                UI.makeJoint(worldVec);
            }
            if(UI.mouseMode == UI.MODE_DELETE)
            {
                Part p = ((net.worlds.object.World)world.getService("World")).getPartAt(worldVec);
                if(p != null && !p.disabled)
                    p.remove();
            }
            if(UI.mouseMode == UI.MODE_ROTATE && UI.getNumberSelected() == 1 && UI.getSelected().get(0) instanceof Part)
            {
                ((Part)UI.getSelected().get(0)).setInUse(true);
                //((Part)UI.getSelected().get(0)).getBody().resetFiltering();
            }
            else if(UI.mouseMode == UI.MODE_GRAB && UI.getNumberSelected() > 0 && UI.getSelected().get(0) instanceof Part)
            {
                if(mj != null && !mj.isDead)
                    world.destroyJoint(mj);
                md = new MouseJointDef();
                md.body1 = world.getGroundBody();
                md.body2 = ((Part)UI.getSelected().get(0)).getBody();
                md.target.set(new Vec2D(x, y).screenToWorld());
                md.maxForce = 2000 * md.body2.m_mass;
                md.frequencyHz = 0.5f;
                md.timeStep = world.getTimestep();
                md.dampingRatio = 0.2f;
                mj = (MouseJoint) world.addJoint(md);
                md.body1.wakeUp();
            }
            else if(UI.mouseMode == UI.MODE_TERRAIN)
            {
                if(world.getWidth() > 2000)
                    System.err.println("Terrain is currently not supported on worlds wider than 2000 units; please try a smaller world.");
                else
                {
                    Terrain ter = (Terrain) world.getService("Terrain");
                    ter.mouseDown(clickVec);
                }
            }
            else if(!foundHandle && UI.mouseMode != UI.MODE_ROPE)
            {
                UI.setMainDragPart(clickedPart);
                Vec2D mouse =  new Vec2D(InputState.mouseX, InputState.mouseClickY).screenToWorld();
                List<GameObject> selected = UI.getSelected();
                synchronized(selected)
                {
                    for(GameObject p : selected)
                    {
                        if(p instanceof Part && !UI.getDragBodies().contains(((Part)p).getBody()) && p != UI.scaleBox)
                            UI.addDragPart(mouse, (Part) p);
                    }
                }
            }
        }
    }

    @Override
    public void mouseClicked(int b, int x, int y) throws WorldsException {
    }

    @Override
    public void mouseUp(int b, int x, int y) throws WorldsException {
        //if(1 == 1)
        //    return;
        final int mouseMode = UI.mouseMode;
        final World world = Main.getGame().getCurrentWorld();
        Main.getView().eMouseUp(b, x, y + 55);
        if(UI.mouseClickUI)
            return;
        Vec2D worldVec = new Vec2D(x, y).screenToWorld();
        if(Main.getLocalPlayer() != null && Main.getLocalPlayer().getSelectedTool() != null)
        {
            Vec2D clickVec = new Vec2D(x, y).screenToWorld();
            Main.getLocalPlayer().getSelectedTool().mouseUp(b, clickVec);
        }
        if(mj != null && !mj.isDead)
        {
            world.destroyJoint(mj);
            mj = null;
        }
        if(b == 1)
        {
            boolean clickedJoint = false;
            for(GameObject j : world.getJoints())
            {
                if(!j.disabled && j.isInWorld())
                {
                    Vec2D pos = ((Vec2D) j.getProp("Anchor")).worldToScreen(Main.getView());
                    if(x > pos.x - 5 && y > pos.y - 5 && x < pos.x+ 5 && y < pos.y + 5)
                    {
                        clickedJoint = true;
                        break;
                    }
                }
            }
            if(UI.mouseMode != UI.MODE_HINGE && UI.mouseMode != UI.MODE_WELD)
            {
                boolean deselected = false;
                synchronized(world.getObjectsByType(Box.class))
                {
                    for(GameObject obj : world.getParts())
                    {
                        Part p = (Part) obj;
                        if(p != null && p.getBody() != null && p.getShape().testPoint(((Part)obj).getBody().getXForm(), worldVec))
                        {
                            if(!p.disabled && p.selected && !obj.newlySelected && UI.shiftPressed && !clickedJoint)
                            {
                                UI.deselect(obj);
                                deselected = true;
                            }
                        }
                    }
                }
                if(deselected)
                    UI.updateSelection();
            }
            synchronized(world.getObjectsByType(Box.class))
            {
                for(GameObject obj : world.getParts())
                {
                    if(obj != null)
                        obj.newlySelected = false;
                }
            }

            if((UI.mouseMode == UI.MODE_BOX || UI.mouseMode == UI.MODE_WATER || UI.mouseMode == UI.MODE_LADDER) && !UI.isDraggingPart())
            {
                    Vec2D mouseClick = new Vec2D(InputState.mouseClickX, InputState.mouseClickY).screenToWorld();
                    Vec2D mouse = new Vec2D(x, y).screenToWorld();
                    if(UI.snapToGrid)
                    {
                        if(mouse.x > mouseClick.x)
                        {
                            mouseClick.x = (float) Math.floor(mouseClick.x);
                            mouse.x = (float) Math.ceil(mouse.x);
                        }
                        else
                        {
                            mouseClick.x = (float) Math.ceil(mouseClick.x);
                            mouse.x = (float) Math.floor(mouse.x);
                        }
                        if(mouse.y < mouseClick.y)
                        {
                            mouseClick.y = (float) Math.ceil(mouseClick.y);
                            mouse.y = (float) Math.floor(mouse.y);
                        }
                        else
                        {
                            mouseClick.y = (float) Math.floor(mouseClick.y);
                            mouse.y = (float) Math.ceil(mouse.y);
                        }
                    }
                    if(Math.abs(mouseClick.x - mouse.x) > 0 && Math.abs(mouseClick.y - mouse.y) > 0)
                    {                        
                        final Color color = UI.getNextObjectColor();
                        final Vec2D dimensions = new Vec2D(Math.abs(mouseClick.x - mouse.x), Math.abs(mouseClick.y - mouse.y));
                        final Vec2D center = new Vec2D((mouse.x + mouseClick.x)/2, (mouse.y + mouseClick.y)/2);

                        UI.action(new RedoRunnable()
                        {
                            @Override
                            public Object run(boolean redo) throws WorldsException {
                                Box box;
                                if(UI.mouseMode == UI.MODE_WATER)
                                    box = (Box)world.createObject("Water");
                                else if(UI.mouseMode == UI.MODE_LADDER)
                                {
                                    box = (Box) world.createObject("Ladder");
                                }
                                else
                                    box = (Box) world.createObject("Box");
                                
                                box.setProp("Position", center, true);
                                if(box instanceof Ladder)
                                    box.setProp("Height", dimensions.y, true);
                                else
                                    box.setProp("Size", dimensions, true);
                                box.setProp("Color", color, true);  
                                box.setParent(world.getService("World"));
                                return box;
                            }
                        },
                        new UndoRunnable()
                        {
                            @Override
                            public void run(Object obj) throws WorldsException {
                                if(((GameObject)obj).isInWorld())
                                    ((GameObject)obj).remove();
                                else
                                    Main.getGame().getByOldID(mouseMode);
                            }
                        }, true);
                    }
                UI.drawingBox = false;
            }
            else if(UI.mouseMode == UI.MODE_SEAT && UI.getNumberSelected() == 0)
            {
                final Vec2D mouse = new Vec2D(x, y).screenToWorld();
                if(UI.snapToGrid)
                {
                    mouse.x = Math.round(mouse.x);
                    mouse.y = (float) (Math.floor(mouse.y) + 0.5);
                }
                UI.action(new RedoRunnable()
                {

                    @Override
                    public Object run(boolean redo) throws WorldsException {
                        Seat seat = (Seat) world.createObject("Seat");
                        seat.setProp("Position", mouse, true);
                        seat.setProp("Color", UI.getNextObjectColor(), true);
                        seat.setParent(world.getService("World"));
                        return seat;
                    }
                }, new UndoRunnable()
                {

                    @Override
                    public void run(Object obj) throws WorldsException {
                        ((Seat) obj).remove();
                    }
                }, false);
            }
            else if(UI.mouseMode == UI.MODE_TERRAIN)
            {
                if(world.getWidth() > 2000)
                   return;
                else
                {
                    Terrain ter = (Terrain) world.getService("Terrain");
                    ter.mouseUp();
                }
            }
            else if(UI.mouseMode == UI.MODE_ROPE)
            {
                final Vec2D startWorld = new Vec2D(InputState.mouseClickX, InputState.mouseClickY).screenToWorld();
                final Vec2D stopWorld = new Vec2D(InputState.mouseX, InputState.mouseY).screenToWorld();
                if(startWorld.sub(stopWorld).length() > 1)
                {
                    UI.action(new RedoRunnable()
                    {

                        @Override
                        public Object run(boolean redo) throws WorldsException {
                            Rope rope = (Rope) Main.getGame().getCurrentWorld().createObject("Rope");
                            rope.setProp("Anchor1", startWorld, true);
                            rope.setProp("Anchor2", stopWorld, true);
                            Part p1 = Main.getGame().getCurrentWorld().getPartAt(startWorld);
                            Part p2 = Main.getGame().getCurrentWorld().getPartAt(stopWorld);
                            if(p1 != null)
                                rope.setProp("Part1", p1, true);
                            if(p2 != null)
                                rope.setProp("Part2", p2, true);
                            if(p1 != null || p2 != null)
                            {
                                rope.setProp("Parent", Main.getGame().getCurrentWorld().getService("JointsService"), true);
                            }
                            return rope;
                        }
                    }, new UndoRunnable()
                    {

                        @Override
                        public void run(Object obj) throws WorldsException {
                            ((Rope)obj).remove();
                        }
                    }, false);
                }
            }
            else if(UI.mouseMode == UI.MODE_CIRCLE && !UI.isDraggingPart())
            {
                    Vec2D mouseClick = new Vec2D(InputState.mouseClickX, InputState.mouseClickY).screenToWorld();
                    Vec2D mouse = new Vec2D(x, y).screenToWorld();
                    if(UI.snapToGrid)
                    {
                        mouse = mouse.round();
                        mouseClick = mouseClick.round();
                    }
                    Vec2D distance = new Vec2D(mouse.x - mouseClick.x, mouse.y - mouseClick.y);
                    final float radius = distance.length();
                    final Color color = UI.getNextObjectColor();
                    if(radius > 1)
                    {
                        final Vec2D finalMouseClick = mouseClick;
                        UI.action(new RedoRunnable()
                        {

                            @Override
                            public Object run(boolean redo) throws WorldsException {
                                Circle c = (Circle) world.createObject("Circle");
                                c.setProp("Radius", radius , true);
                                c.setProp("Position", finalMouseClick, true);
                                c.setProp("Color", color, true);
                                c.setParent(world.getService("World"));
                                return c;
                            }
                        }, new UndoRunnable()
                        {

                            @Override
                            public void run(Object obj) throws WorldsException {
                                ((Circle)obj).remove();
                            }
                        }, false);
                    }
                    UI.drawingCircle = false;
            }
            else if(UI.mouseMode == UI.MODE_TRIANGLE && !UI.isDraggingPart())
            {
                Vec2D mouseClick = new Vec2D(InputState.mouseClickX, InputState.mouseClickY).screenToWorld();
                Vec2D mouse = new Vec2D(x, y).screenToWorld();
                if(UI.snapToGrid)
                {
                    if(mouse.x > mouseClick.x)
                    {
                        mouseClick.x = (float) Math.floor(mouseClick.x);
                        mouse.x = (float) Math.ceil(mouse.x);
                    }
                    else
                    {
                        mouseClick.x = (float) Math.ceil(mouseClick.x);
                        mouse.x = (float) Math.floor(mouse.x);
                    }
                    if(mouse.y < mouseClick.y)
                    {
                        mouseClick.y = (float) Math.ceil(mouseClick.y);
                        mouse.y = (float) Math.floor(mouse.y);
                    }
                    else
                    {
                        mouseClick.y = (float) Math.floor(mouseClick.y);
                        mouse.y = (float) Math.ceil(mouse.y);
                    }
                }
                final Vec2D distance = new Vec2D(mouse.x - mouseClick.x, mouse.y - mouseClick.y).abs();
                if(distance.x > 0 && distance.y > 0)
                {
                    final Vec2D center = (mouse.add(mouseClick)).div(2);
                    final Color color = UI.getNextObjectColor();
                    
                    String d2 = mouse.x > mouseClick.x ? "R" : "L";
                    String d1 = mouse.y > mouseClick.y ? "U" : "D";
                    final String direction = d1+d2;
                    UI.action(new RedoRunnable()
                    {

                        @Override
                        public Object run(boolean redo) throws WorldsException {
                            Triangle t = (Triangle) world.createObject("Triangle");
                            t.setProp("Direction", direction, true);
                            t.setProp("Position", center, true);
                            t.setProp("Size", distance, true);
                            t.setProp("Color", color, true);
                            t.setParent(world.getService("World"));
                            return t;
                        }
                    }, new UndoRunnable()
                    {

                        @Override
                        public void run(Object obj) throws WorldsException {
                            ((Triangle)obj).remove();
                        }
                    }, false);
                }
                UI.drawingTriangle = false;
            }
            else if(UI.mouseMode == UI.MODE_ARROW && !UI.isDraggingPart() && (Main.getLocalPlayer() == null || Main.getLocalPlayer().getSelectedTool() == null))
            {
                int startX;
                int startY;
                int stopX;
                int stopY;
                if(InputState.mouseClickX < InputState.mouseX)
                {
                    startX = InputState.mouseClickX;
                    stopX = InputState.mouseX;
                }
                else
                {
                    startX = InputState.mouseX;
                    stopX = InputState.mouseClickX;
                }
                if(InputState.mouseClickY > InputState.mouseY)
                {
                    startY = InputState.mouseClickY;
                    stopY = InputState.mouseY;
                }
                else
                {
                    startY = InputState.mouseY;
                    stopY = InputState.mouseClickY;
                }
                int width = Math.abs(stopX - startX);
                int height = Math.abs(stopY - startY);
                if(width < 5 || height < 5)
                    return;
                Vec2D start = new Vec2D(startX, startY).screenToWorld();
                Vec2D stop = new Vec2D(stopX, stopY).screenToWorld();
                AABB bounds = new AABB();
                bounds.lowerBound = start;
                bounds.upperBound = stop;
                Shape[] shapes = world.query(bounds, WorldsSettings.MAX_OBJECTS);
                for(Shape s : shapes)
                {
                    Part p = s.getUserData();
                    if(p != null && !p.disabled)
                    UI.select(p);
                }
                UI.updateSelection();
            }
            UI.clearDragParts();

            if(UI.mouseMode == UI.MODE_ROTATE && UI.getNumberSelected() == 1 && UI.getSelected().get(0) instanceof Part)
            {
                ((Part)UI.getSelected().get(0)).setInUse(false);
                //((Part)UI.getSelected().get(0)).getBody().resetFiltering();
            }
            else if(UI.scaleBox != null && UI.mouseMode == UI.MODE_SCALE)
            {
                final Box box = UI.scaleBox;
                final Vec2D oldPosition = box.getPosition();
                final Vec2D oldSize = box.getVec2DProp("Size");
                box.setInUse(false);
                //box.getBody().resetFiltering();
                if(UI.snapToGrid)
                    worldVec = worldVec.round();
                Vec2D local = box.getBody().getLocalPoint(worldVec);
                Vec2D orig = UI.scaleVert;
                Vec2D opposite = UI.scaleOppositeVert;
                Vec2D distance = new Vec2D(local.sub(opposite)).abs();
                if(distance.x == 0.0)
                    distance.x = 0.25f;
                if(distance.y == 0.0)
                    distance.y = 0.25f;
                final Vec2D finalDistance = distance;
                final Vec2D vec = worldVec.add(new Vec2D(box.getBody().getWorldPoint(opposite))).div(2);
                UI.action(new RedoRunnable()
                {

                    @Override
                    public Object run(boolean redo) throws WorldsException {
                        box.setProp("Size", finalDistance, true);
                        if(box.getBody().getParts().length == 1)
                            box.setProp("Position", vec, true);
                        else
                            box.setShapePosition(vec);
                        return null;
                    }
                }, new UndoRunnable()
                {

                    @Override
                    public void run(Object obj) throws WorldsException {
                        if(box.getBody().getParts().length == 1)
                            box.setProp("Position", oldPosition, true);
                        else
                            box.setShapePosition(oldPosition);
                        box.setProp("Size", oldSize, true);
                    }
                }, false);
                UI.scaleBox = null;
                UI.scaleVert = null;
            }
        }
    }

    @Override
    public void mouseMoved(int x, int y) throws WorldsException {
        if(Main.getView().eMouseMove(x, y + 55))
            return;
        if(Main.isBuildClient())
        {
            Main.getClient().sendCursorPosition(new Vec2D(x, y).screenToWorld());
        }
    }

    @Override
    public void mouseDragged(int x, int y, int btn) throws WorldsException {
        World world = Main.getGame().getCurrentWorld();
        if(Main.getView().eMouseDrag(btn, x, y + 55))
            return;
        if(UI.mouseOverUI)
            return;
        Vec2D clickVec = new Vec2D(x, y).screenToWorld();
        if(Main.getLocalPlayer() != null && Main.getLocalPlayer().getSelectedTool() != null)
        {
            Main.getLocalPlayer().getSelectedTool().mouseDragged(btn, clickVec);
        }
        synchronized(world.getObjectsByType(Box.class))
        {
            for(GameObject obj : world.getParts())
            {
                Part p = (Part) obj;
                if(p != null && p.getBody() != null && !p.disabled && !(UI.drawOver && (UI.mouseMode == UI.MODE_CIRCLE || UI.mouseMode == UI.MODE_BOX)) && !obj.getBooleanProp("Locked") && p.getShape().testPoint(((Part)obj).getBody().getXForm(), clickVec))
                {
                    p.getEvent("Dragged").fire(new Object[]{Main.getPlayer(), btn});
                }
            }
        }
        if(btn == 1)
        {
            if(UI.mouseMode == UI.MODE_GRAB && mj != null)
            {
                mj.setTarget(clickVec);
            }
            else if(UI.mouseMode == UI.MODE_ROTATE && UI.getSelected().size() == 1 && UI.getSelected().get(0) instanceof Part)
            {
                Part p = (Part) UI.getSelected().get(0);
                Vec2D bodyPos = new Vec2D(p.getBody().getPosition());
                Vec2D vec = new Vec2D(InputState.mouseX, InputState.mouseY).add(bodyPos.worldToScreen(Main.getView()).mul(-1));
                float mouseAngle = vec.toAngle();
                float oldAngle = new Vec2D(InputState.mouseClickX, InputState.mouseClickY).add(bodyPos.worldToScreen(Main.getView()).mul(-1)).toAngle();
                float newAngle = (float) (p.getOldRotation() - Math.toDegrees(mouseAngle) + Math.toDegrees(oldAngle));
                if(UI.shiftPressed)
                    newAngle = Util.roundTo45(newAngle);
                if(!(p instanceof Water) && !(p instanceof Ladder))
                    p.setProp("Rotation", newAngle, true);
            }
            else if(UI.mouseMode == UI.MODE_TERRAIN)
            {
                if(world.getWidth() > 2000)
                    return;
                else
                {
                    Terrain ter = (Terrain) world.getService("Terrain");
                    ter.mouseDrag(clickVec);
                }
            }
            else
            {
                UI.processDragParts(clickVec);
            }
        }
        if(btn == 3)
        {
            Vec2D vec = new Vec2D(InputState.pmouseX - x, y - InputState.pmouseY).toWorldUnits();
            Main.getView().translateCam(vec);
        }
    }

    @Override
    public void mouseWheelMoved(int wheelRotation) throws WorldsException {
        if(Main.getLocalPlayer() == null)
        {
            ((AssetService)Main.getGame().getMainWorld().getService("AssetService")).playSound("Click2");
            Main.getView().setScaleFactor(Main.getView().scaleFactor - Main.getView().scaleFactor * wheelRotation / 10);
        }
    }
}
